"""
Test lldb data formatter subsystem.
"""

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class CategoriesDataFormatterTestCase(TestBase):
    def setUp(self):
        # Call super's setUp().
        TestBase.setUp(self)
        # Find the line number to break at.
        self.line = line_number("main.cpp", "// Set break point at this line.")

    @expectedFlakeyNetBSD
    def test_with_run_command(self):
        """Test that that file and class static variables display correctly."""
        self.build()
        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)

        lldbutil.run_break_set_by_file_and_line(
            self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True
        )

        self.runCmd("run", RUN_SUCCEEDED)

        # The stop reason of the thread should be breakpoint.
        self.expect(
            "thread list",
            STOPPED_DUE_TO_BREAKPOINT,
            substrs=["stopped", "stop reason = breakpoint"],
        )

        # This is the function to remove the custom formats in order to have a
        # clean slate for the next test case (most of these categories do not
        # exist anymore, but we just make sure we delete all of them)
        def cleanup():
            self.runCmd("type format clear", check=False)
            self.runCmd("type summary clear", check=False)
            self.runCmd("type category delete Category1", check=False)
            self.runCmd("type category delete Category2", check=False)
            self.runCmd("type category delete NewCategory", check=False)
            self.runCmd("type category delete CircleCategory", check=False)
            self.runCmd("type category delete RectangleStarCategory", check=False)
            self.runCmd("type category delete BaseCategory", check=False)

        # Execute the cleanup function during test case tear down.
        self.addTearDownHook(cleanup)

        # Add a summary to a new category and check that it works
        self.runCmd(
            'type summary add Rectangle --summary-string "ARectangle" -w NewCategory'
        )

        self.expect(
            "frame variable r1 r2 r3",
            matching=False,
            substrs=["r1 = ARectangle", "r2 = ARectangle", "r3 = ARectangle"],
        )

        self.runCmd("type category enable NewCategory")

        self.expect(
            "frame variable r1 r2 r3",
            matching=True,
            substrs=["r1 = ARectangle", "r2 = ARectangle", "r3 = ARectangle"],
        )

        # Disable the category and check that the old stuff is there
        self.runCmd("type category disable NewCategory")

        self.expect("frame variable r1 r2 r3", substrs=["r1 = {", "r2 = {", "r3 = {"])

        # Re-enable the category and check that it works
        self.runCmd("type category enable NewCategory")

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = ARectangle", "r2 = ARectangle", "r3 = ARectangle"],
        )

        # Delete the category and the old stuff should be there
        self.runCmd("type category delete NewCategory")

        self.expect("frame variable r1 r2 r3", substrs=["r1 = {", "r2 = {", "r3 = {"])

        # Add summaries to two different categories and check that we can
        # switch
        self.runCmd(
            'type summary add --summary-string "Width = ${var.w}, Height = ${var.h}" Rectangle -w Category1'
        )
        self.runCmd(
            "type summary add --python-script \"return 'Area = ' + str( int(valobj.GetChildMemberWithName('w').GetValue()) * int(valobj.GetChildMemberWithName('h').GetValue()) );\" Rectangle -w Category2"
        )

        # check that enable A B is the same as enable B enable A
        self.runCmd("type category enable Category1 Category2")

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Width = ", "r2 = Width = ", "r3 = Width = "],
        )

        self.runCmd("type category disable Category1")

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Area = ", "r2 = Area = ", "r3 = Area = "],
        )

        # switch again

        self.runCmd("type category enable Category1")

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Width = ", "r2 = Width = ", "r3 = Width = "],
        )

        # Re-enable the category and show that the preference is persisted
        self.runCmd("type category disable Category2")
        self.runCmd("type category enable Category2")

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Area = ", "r2 = Area = ", "r3 = Area = "],
        )

        # Now delete the favorite summary
        self.runCmd("type summary delete Rectangle -w Category2")

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Width = ", "r2 = Width = ", "r3 = Width = "],
        )

        # Delete the summary from the default category (that does not have it)
        self.runCmd("type summary delete Rectangle", check=False)

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Width = ", "r2 = Width = ", "r3 = Width = "],
        )

        # Now add another summary to another category and switch back and forth
        self.runCmd("type category delete Category1 Category2")

        self.runCmd(
            'type summary add Rectangle -w Category1 --summary-string "Category1"'
        )
        self.runCmd(
            'type summary add Rectangle -w Category2 --summary-string "Category2"'
        )

        self.runCmd("type category enable Category2")
        self.runCmd("type category enable Category1")

        self.runCmd("type summary list -w Category1")
        self.expect(
            "type summary list -w NoSuchCategoryHere",
            substrs=["no matching results found"],
        )

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Category1", "r2 = Category1", "r3 = Category1"],
        )

        self.runCmd("type category disable Category1")

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Category2", "r2 = Category2", "r3 = Category2"],
        )

        # Check that re-enabling an enabled category works
        self.runCmd("type category enable Category1")

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Category1", "r2 = Category1", "r3 = Category1"],
        )

        self.runCmd("type category delete Category1")
        self.runCmd("type category delete Category2")

        self.expect("frame variable r1 r2 r3", substrs=["r1 = {", "r2 = {", "r3 = {"])

        # Check that multiple summaries can go into one category
        self.runCmd(
            'type summary add -w Category1 --summary-string "Width = ${var.w}, Height = ${var.h}" Rectangle'
        )
        self.runCmd(
            'type summary add -w Category1 --summary-string "Radius = ${var.r}" Circle'
        )

        self.runCmd("type category enable Category1")

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Width = ", "r2 = Width = ", "r3 = Width = "],
        )

        self.expect(
            "frame variable c1 c2 c3",
            substrs=["c1 = Radius = ", "c2 = Radius = ", "c3 = Radius = "],
        )

        self.runCmd("type summary delete Circle -w Category1")

        self.expect("frame variable c1 c2 c3", substrs=["c1 = {", "c2 = {", "c3 = {"])

        # Add a regex based summary to a category
        self.runCmd(
            'type summary add -w Category1 --summary-string "Radius = ${var.r}" -x Circle'
        )

        self.expect(
            "frame variable r1 r2 r3",
            substrs=["r1 = Width = ", "r2 = Width = ", "r3 = Width = "],
        )

        self.expect(
            "frame variable c1 c2 c3",
            substrs=["c1 = Radius = ", "c2 = Radius = ", "c3 = Radius = "],
        )

        # Delete it
        self.runCmd("type summary delete Circle -w Category1")

        self.expect("frame variable c1 c2 c3", substrs=["c1 = {", "c2 = {", "c3 = {"])

        # Change a summary inside a category and check that the change is
        # reflected
        self.runCmd('type summary add Circle -w Category1 --summary-string "summary1"')

        self.expect(
            "frame variable c1 c2 c3",
            substrs=["c1 = summary1", "c2 = summary1", "c3 = summary1"],
        )

        self.runCmd('type summary add Circle -w Category1 --summary-string "summary2"')

        self.expect(
            "frame variable c1 c2 c3",
            substrs=["c1 = summary2", "c2 = summary2", "c3 = summary2"],
        )

        # Check that our order of priority works. Start by clearing categories
        self.runCmd("type category delete Category1")

        self.runCmd('type summary add Shape -w BaseCategory --summary-string "AShape"')
        self.runCmd("type category enable BaseCategory")

        self.expect("expression (Shape*)&c1", substrs=["AShape"])
        self.expect("expression (Shape*)&r1", substrs=["AShape"])
        self.expect("expression (Shape*)c_ptr", substrs=["AShape"])
        self.expect("expression (Shape*)r_ptr", substrs=["AShape"])

        self.runCmd(
            'type summary add Circle -w CircleCategory --summary-string "ACircle"'
        )
        self.runCmd(
            'type summary add Rectangle -w RectangleCategory --summary-string "ARectangle"'
        )
        self.runCmd("type category enable CircleCategory")

        self.expect("frame variable c1", substrs=["ACircle"])
        self.expect("frame variable c_ptr", substrs=["ACircle"])

        self.runCmd(
            'type summary add "Rectangle *" -w RectangleStarCategory --summary-string "ARectangleStar"'
        )
        self.runCmd("type category enable RectangleStarCategory")

        self.expect(
            "frame variable c1 r1 c_ptr r_ptr", substrs=["ACircle", "ARectangleStar"]
        )

        self.runCmd("type category enable RectangleCategory")

        self.expect(
            "frame variable c1 r1 c_ptr r_ptr",
            substrs=["ACircle", "ACircle", "ARectangle"],
        )

        # Check that abruptly deleting an enabled category does not crash us
        self.runCmd("type category delete RectangleCategory")

        self.expect(
            "frame variable c1 r1 c_ptr r_ptr",
            substrs=[
                "ACircle",
                "(Rectangle) r1 = ",
                "w = 5",
                "h = 6",
                "ACircle",
                "ARectangleStar",
            ],
        )

        # check that list commands work
        self.expect("type category list", substrs=["RectangleStarCategory (enabled)"])

        self.expect("type summary list", substrs=["ARectangleStar"])

        # Disable a category and check that it fallsback
        self.runCmd("type category disable CircleCategory")

        # check that list commands work
        self.expect("type category list", substrs=["CircleCategory (disabled"])

        self.expect("frame variable c1 r_ptr", substrs=["AShape", "ARectangleStar"])

        # check that filters work into categories
        self.runCmd("type filter add Rectangle --child w --category RectangleCategory")
        self.runCmd("type category enable RectangleCategory")
        self.runCmd(
            'type summary add Rectangle --category RectangleCategory --summary-string " " -e'
        )
        self.expect("frame variable r2", substrs=["w = 9"])
        self.runCmd('type summary add Rectangle --summary-string " " -e')
        self.expect("frame variable r2", matching=False, substrs=["h = 16"])

        # Now delete all categories
        self.runCmd(
            "type category delete CircleCategory RectangleStarCategory BaseCategory RectangleCategory"
        )

        # check that a deleted category with filter does not blow us up
        self.expect("frame variable r2", substrs=["w = 9", "h = 16"])

        # and also validate that one can print formatters for a language
        self.expect(
            "type summary list -l c++", substrs=["vector", "map", "list", "string"]
        )
